#!/usr/bin/python2.4
#
# DbObject is a wrapper over afe/tko's db objects.


class DbObject(object):

  __slots__ = 'raw_object', 'overrides'

  class __metaclass__(type):

    def __init__(self, name, bases, dicts):
      self.all_slots = []
      for cls in self.__mro__[:-1]:
        if isinstance(cls.__slots__, basestring):
          self.all_slots.append(cls.__slots__)
        else:
          self.all_slots.extend(cls.__slots__)

  def __init__(self, raw_object, **dargs):
    self.raw_object = raw_object
    if dargs:
      self.overrides = dargs
    else:
      self.overrides = None

  def __repr__(self):
    results = ['%s=%r' % (name, getattr(self, name))
               for name in type(self).all_slots[:-len(DbObject.__slots__)]]
    results.append('raw_object=%r' % (self.raw_object,))
    if self.overrides:
      results.extend('%s=%r' % kv for kv in self.overrides.iteritems())
    return '%s(%s)' % (type(self).__name__, ', '.join(results))

  def __getattr__(self, name):
    if self.overrides and name in self.overrides:
      return self.overrides[name]

    try:
      return self[name]
    except KeyError:
      raise AttributeError('%r object has no attribute %r' % (
                           type(self).__name__, name))

  def __setattr__(self, name, value):
    if name in type(self).all_slots:
      return super(DbObject, self).__setattr__(name, value)

    if self.overrides is None:
      self.overrides = {}
    self.overrides[name] = value

  def __delattr__(self, name):
    try:
      del self.overrides[name]
    except (TypeError, KeyError):
      raise AttributeError('%r object has no attribute %r' % (
                           type(self).__name__, name))
    if not self.overrides:
      self.overrides = None

  def __getitem__(self, key):
    return self.raw_object[key]

  def __setitem__(self, key, value):
    self.raw_object[key] = value


class TupleDbObject(DbObject):

  __slots__ = ()
  KEY_TO_INDEX = {}

  def __getitem__(self, key):
    if isinstance(key, (int, long, slice)):
      return self.raw_object[key]
    else:
      return self.raw_object[self.KEY_TO_INDEX[key]]

  def __setitem__(self, key, value):
    raise TypeError('object does not support item assignment')
